const RBAC = require('./rbac-a');
const Provider = RBAC.providers.JsonProvider;
// https://github.com/yanickrochon/rbac-a

module.exports = function (options, app) {
	
	const getRules = async function () {
		// 任何权限配置修改之后必须重新启动服务
		
		// https://www.npmjs.com/package/rbac-a
		const roles = await think.model('role').select();
		const users = await think.model('user').field('id,username').setRelation(false).select();
		const rules = {roles: {}, users: {}};
		if(roles) {
			for (const role of roles) {
				let permissions = await think.model('role_auth').where({role_id: role.id}).setRelation(false).getField('id');
				// permissions=permissions.join(',').split(',');
				if(permissions.length > 0) {
					permissions = permissions.join(',').split(',');
				} else {
					permissions = [];
				}
				rules.roles[role.name] = {
					'inherited': role.parent ? [role.parent.name] : [],
					'permissions': permissions || [],
					'attributes': []
				};
			}
		}
		if(users) {
			for (const user of users) {
				let roles1 = await think.model('role')
				.alias('r')
				.join({
					table: 'user_role',
					join: 'left',
					as: 'ur',
					on: ['r.id', 'ur.role_id']
					// field:'uid'
				})
				.where({'ur.uid': user.id})
				.setRelation(false).getField('name');
				
				roles1 = roles1.join(',').split(',');
				
				rules.users[user.username] = roles1 || [];
			}
		}
		think.logger.debug(rules);
		
		return rules;
	};
	
	// 检测用户是都有权限访问指定的路由相应的方法
	RBAC.prototype.checkPath = async function (user, path, method = 'get') {
		// 找出所有能够访问该路径的角色列表
		const map = {};
		map[`ra.${method}`] = 1;
		map['r.path'] = path;
		
		const entity = await think.model('role_auth')
		.alias('ra')
		.join({
			table: 'resource',
			join: 'left',
			as: 'r',
			on: ['r.id', 'ra.resource_id']
		})
		.where(map)
		.getField('ra.id');
		
		if(!think.isEmpty(entity)) {
			//think.logger.debug(entity);
			// entity='8f221801-08b8-11e9-acd4-00ff5adc8e6e';
			
			try {
				return await this.check(user, entity);
			}catch (e) {
				return false;
			}
			
			
			//const allowed = await this.check(user, entity).then(function (allowed) {
			//	if(allowed) {
			//		think.logger.debug('通过授权检测');
			//		return true;
			//	} else {
			//		// think.logger.error('没通过授权检测');
			//		return false;
			//	}
			//	// return allowed;
			//}).catch(function (err) {
			//	think.logger.error(err && err.stack || err || 'ERROR');
			//	return false;
			//});
			
			//return allowed;
		}
		return false;
	};
	
	// 注入权限检查机制
	//think.rbac = new RBAC({
	//  provider: new Provider(rules)
	//});
	
	return async (ctx, next) => {
		const module = ctx.module;
		const controller = ctx.controller;
		const action = ctx.action.toUpperCase();
		// 只支持以下方法
		if(['GET', 'POST', 'DELETE', 'PUT', 'PATCH'].indexOf(action) === -1) {
			return ctx.fail(1000, `资源没有授权访问`, '');
		}
		
		// const data = {
		//	controller: ctx.controller,
		//	action: ctx.action,
		//	module: ctx.module,
		//	url: ctx.url
		// };
		
		// const data = {
		//	controller: 'role',
		//	action: 'get',
		//	module: 'admin',
		//	url: '/admin/role?_t=1545738551&page=1&key=&pageSize=10'
		// }
		
		const path = `/${module}/${controller}`;
		
		// 不验证的路由
		if(path.match(/^\/admin\/token/)) {
			// 注册登录
			return next();
		}
		
		// 不验证的路由
		if(path.match(/^\/admin\/user/)) {
			// 注册登录
			if (action==='POST'){
				return next();
			}
		}
		
	
		//开发中的模块，直接跳过权限检测
		if(path.match(/^\/supervision\/admin/)) {
			// 注册登录
			return next();
		}
		
		
		// 检测登录的用户信息
		let userInfo = {};
		try {
			userInfo = await ctx.session('userInfo');
		} catch (err) {
			userInfo = {};
		}
		
		if(think.isEmpty(userInfo) || think.isEmpty(userInfo.username)) {
			return ctx.throw(401, `请登录以后访问1。`);
			// this.ctx.status = 401;
			// return this.ctx.fail(-1, '请登录后操作');
		}
		//think.logger.debug(userInfo);
		
		// 检测RESTful接口的调用权限
		//以下代码每一次请求都会调用，频率非常高，需要优化
		
		//如果缓存存在，直接读取缓存
		let rules = await ctx.cache('rbac_rules', () => {
			return getRules();
		}, {
			//timeout: 24 * 60 * 60 * 1000
			timeout: 10 * 60 * 1000 //10分钟
		});
		const rbac = new RBAC({
			provider: new Provider(rules) //需要动态获取规则，后台修改授权必须重启服务才能使规则生效
		});
		const auth = await rbac.checkPath(userInfo.username, path, action);
		
		//const auth = await think.rbac.checkPath(userInfo.username, path, action);
		if(!auth) {
			//think.logger.error(`没有授权访问”${path}“`);
			// ctx.status = 200;
			return ctx.fail(1000, `没有授权访问”${path}“`);
		}
		
		// 检测菜单访问权限
		
		// 检测其它访问权限
		
		return next();
	};
};
